package org.gpswalker.sys.server.components;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;

import com.google.appengine.api.datastore.Key;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class GpxTrack {

    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key id;

    public static final int LISTLIMIT = 10;

    @Persistent
    private Date d;
    @Persistent
    private Date end;

    @Persistent
    private double avgSpeed;

    @Persistent
    private String creator;
    @Persistent
    private String name;
    @Persistent
    private double up;
    @Persistent
    private double down;
    @Persistent
    private double distfrom = 0.0;
    @Persistent
    private String description;
    @Persistent
    private double maxSpeed = 0.0;

    @Persistent
    private String XML;

    @Persistent
    private int type = TTypes.WALKING.intValue();

    private GpxTrack() {
    }

    public GpxTrack(String xml) throws JDOMException, IOException {

	XML = xml;

	List<Double> speeds = new ArrayList<Double>();

	double firstElevation = -10000000;
	double firstLon = 400.0;
	double firstLat = 400.0;
	double curspeed = 0.0;
	double intSp = 0.0;

	double substract = 0.0;

	double totalDeltaLat = 0.0;
	double totalDeltaLon = 0.0;

	double currentDist = 0.0;
	Calendar lastMoment = null;
	Calendar thisMom = null;
	long timeSLast = 0;

	SAXBuilder builder = new SAXBuilder();
	Reader in = new StringReader(xml);
	Document doc = null;
	Element root = null;
	doc = builder.build(in);
	root = doc.getRootElement();
	Namespace ns = root.getNamespace();

	creator = root.getAttributeValue("creator");

	Element trk = root.getChild("trk", ns);
	name = trk.getChildTextTrim("name", ns);
	description = trk.getChildTextTrim("desc", ns);

	List<Element> trackSegments = (List<Element>) trk.getChildren("trkseg",
		ns);

	for (Element seg : trackSegments) {

	    List<Element> trackPoints = seg.getChildren("trkpt", ns);
	    if (trackPoints.size() == 0) {
		throw new NullPointerException("trackpoints are empty");
	    }
	    end = GPS.parseXSD(
		    trackPoints.get(trackPoints.size() - 1).getChildTextTrim(
			    "time", ns)).getTime();

	    for (Element trkpt : trackPoints) {

		float el = Float.parseFloat(trkpt.getChild("ele", ns)
			.getTextTrim());
		double lon = Double.parseDouble(trkpt.getAttributeValue("lon"));
		double lat = Double.parseDouble(trkpt.getAttributeValue("lat"));

		if (firstLon == 400.0) {
		    firstLon = lon;
		    firstLat = lat;
		    lastMoment = GPS.parseXSD(trkpt.getChildText("time", ns));
		    d = lastMoment.getTime();
		    firstElevation = el;
		} else {
		    totalDeltaLat += Math.abs(firstLat - lat);
		    totalDeltaLon += Math.abs(firstLon - lon);
		    currentDist = GPS.distFrom(firstLat, firstLon, lat, lon) / 1000.0;
		    thisMom = GPS.parseXSD(trkpt.getChildText("time", ns));
		    timeSLast = thisMom.getTimeInMillis()
			    - lastMoment.getTimeInMillis();

		    double a = timeSLast / 1000.0 / 60.0 / 60.0;

		    curspeed = currentDist / a;

		    speeds.add(curspeed);
		    if (speeds.size() > LISTLIMIT) {
			substract = speeds.remove(0);
		    }

		    intSp += curspeed - substract;
		    curspeed = intSp / speeds.size();

		    maxSpeed = max(maxSpeed, curspeed);

		    distfrom += currentDist;

		    firstLat = lat;
		    firstLon = lon;
		    lastMoment = thisMom;

		    if (el > firstElevation) {
			up += el - firstElevation;
		    } else {
			down += (firstElevation - el);
		    }
		    firstElevation = el;
		}
	    }
	}

	setAvgSpeed();

    }

    public String creator() {
	return creator;
    }

    public String name() {
	return name;
    }

    public double up() {
	return up;
    }

    public double down() {
	return down;
    }

    public double distance() {
	return distfrom;
    }

    public String description() {
	return description;
    }

    public Date date() {
	return d;
    }

    public double maxSpeed() {
	return maxSpeed;
    }

    private double max(double a, double b) {
	if (Double.isNaN(a)) {
	    return b;
	}
	if (Double.isNaN(b)) {
	    return a;
	}
	return Math.max(a, b);
    }

    private void setAvgSpeed() {
	int timespan = (int) (end.getTime() - d.getTime());
	avgSpeed = distfrom / (timespan / 1000.0 / 60.0 / 60.0);
    }

    public double avgspeed() {
	return avgSpeed;
    }
}
